#include <iostream>
#include <unistd.h>
#include <vector>
#include <string.h>
#include <string>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>

using namespace std;

// 函数指针数组
typedef void (*task)();
vector<task> tasks;


void task1()
{
    cout << "任务1    " << endl;
}
void task2()
{
    cout << "任务2    " << endl;
}
void task3()
{
    cout << "任务3    " << endl;
}
void task4()
{
    cout << "任务4    " << endl;
}

void LoadTask(vector<task> *tasks)
{
    tasks->push_back(task1);
    tasks->push_back(task2);
    tasks->push_back(task3);
    tasks->push_back(task4);
}

void Menu()
{
    std::cout << "**********************************************" << std::endl;
    std::cout << "*********** 1. 任务1       ***************" << std::endl;
    std::cout << "*********** 2. 任务2   ***************" << std::endl;
    std::cout << "*********** 3. 任务3     ***************" << std::endl;
    std::cout << "*********** 4. 任务4     ***************" << std::endl;
    std::cout << "*********** 0. 退出            ***************" << std::endl;
    std::cout << "**********************************************" << std::endl;
    std::cout << "Please Enter@" << std::endl;
}

// 管道类
class channel
{
public:
    channel(int wfd, int id, const string name)
        : _wfd(wfd), _id(id), _name(name)
    {
    }

public:
    int _wfd;     // 管道的写文件描述符
    int _id;      // 子进程的id
    string _name; // 子进程的名字
};

void writer(int fd)
{
    // 文件已经打开，直接写入
    string str = "hello I am child";
    char buffer[1024];
    int num = 0;
    char ch = 'c';
    while (1)
    {
        // snprintf(buffer,sizeof(buffer),"%s-%d-%d",str.c_str(),getpid(),num++);
        write(fd, buffer, strlen(buffer));
        sleep(1);
    }
}

void readr()
{
    while (1)
    {
        int task_code = 0;
        int n = read(0, &task_code, sizeof(int));
        if (n == sizeof(int))
        {
            cout << "I am chid, pid: " << getpid() << ",I access a task_code : " << task_code << endl;
            // 在分配任务阶段进行任务码检查
            tasks[task_code - 1](); // 函数指针数组中调用函数需要加上()
        }
        if (n == 0)
            break;
    }
}

// 初始化管道
void InitProcessPool(vector<channel> &channels)
{
    vector<int> oldfd;
    for (int i = 0; i < 5; i++)
    {
        int pipefd[2];
        // 创建管道
        int n = pipe(pipefd);
        if (n < 0)
        {
            perror("pipe");
            return;
        }

        pid_t id = fork();
        if (id < 0)
        {
            perror("fork");
            return;
        }

        // 子进程用来读，父进程用来写
        else if (id == 0)
        {
            //解决多个子进程的写端指向上一个子进程
          
            for(auto fd:oldfd)
            {
                close(fd);
                cout<<fd<<" ";
            }
            cout<<endl;


            close(pipefd[1]);
            dup2(pipefd[0], 0); // 输入重定向，子进程从管道进行输入
            readr();            // 读取管道内的任务码并调用进程实现任务
            close(pipefd[0]);
            exit(0);
        }
        else
        {
            close(pipefd[0]);
        }
        string name = "process :" + to_string(i + 1);
        channels.push_back(channel(pipefd[1], id, name));
        oldfd.push_back(pipefd[1]);
    }
}

// 分配任务
void allocate(vector<channel> &channels)
{
    // 采用随机数分配
    Menu();
    srand((unsigned int)time(0));

    int n = rand() % channels.size();
    int task_code = 0;
    while (1)
    {
        cin >> task_code;
        if (task_code >= 1 && task_code <= tasks.size())
        {
            write(channels[n]._wfd, &task_code, sizeof(int));
        }
        else if (task_code == 0)
        {
            cout << "退出" << endl;
            break;
        }
        else
            cout << "选择错误，重新选择" << endl;

        sleep(1);
        n = rand() % channels.size();
    }
}

// 回收进程
void RecycleProcess(vector<channel> &channels)
{
    //第一个进程由于还有其他的写入端，导致它的管道一直在read时候阻塞了。
    //从而进程无法退出，导致卡住了
    // 如果我们就想要在一个循环里面进行回收，我们可以使用倒着的for循环
    //还有个解决方法从源头解决：将当前子进程指向上一个子进程的写fd记录下来，然后进行关闭
    for (const auto &c : channels)
    {
        close(c._wfd);
        waitpid(c._id, nullptr, 0);
    }

    // int last = channels.size()-1;
    // for(int i = last; i >= 0; i--)
    // {
    //     close(channels[i]._cmdfd);
    //     waitpid(channels[i]._slaverid, nullptr, 0);
    // }

    // for(const auto &c : channels) close(c._wfd);
    // sleep(5);
    // for(const auto &c : channels) waitpid(c._id, nullptr, 0);
    // sleep(5);
}

// 实现进程池
int main()
{
    // 加载任务
    LoadTask(&tasks);
    // 1.创建多个进程
    vector<channel> channels;
    InitProcessPool(channels);

    // 2.选定某个进程进行分配任务
    allocate(channels);

    // 子进程中负责读取任务码并执行

    // 3.回收进程
    RecycleProcess(channels);

    // for(auto& e : channels)
    // {
    //     cout << e._wfd << ", " << e._id << ", " << e._name <<endl;
    // }

    return 0;
}